#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
// #include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <linux/of_irq.h>

#define KEY_CNT 1
#define KEY_NAME "key"

enum KEY_STATUS
{
    KEY_PRESS = 0, // 按键按下
    KEY_RELEASE,   // 按键释放
    KEY_KEEP,      // 按键保持
};

struct key_dev
{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    int key_gpio;           // 按键引脚
    struct timer_list timer; /* 按键值 */
    int irq_num;             // 中断号
    spinlock_t lock;
};

static struct key_dev key; /* 按键设备 */
static int status = KEY_KEEP;


/**
 * key_irq_handler - 按键中断处理函数
 * @irq: 中断号
 * @dev_id: 设备标识符指针
 *
 * 该函数作为按键中断的服务例程，当按键产生中断时被调用。
 * 函数主要功能是启动防抖定时器，在中断发生后20毫秒后触发定时器处理函数。
 *
 * 返回值: IRQ_HANDLED表示中断已成功处理
 */
static irqreturn_t key_irq_handler(int irq, void *dev_id)
{
    /* 启动防抖定时器，延迟20毫秒后执行 */
    mod_timer(&key.timer, jiffies + msecs_to_jiffies(20));
    return IRQ_HANDLED;
}


/**
 * @brief 解析设备树中的按键节点配置信息
 * 
 * 该函数用于从设备树中获取按键相关的配置参数，包括：
 * 1. 查找设备树中的/key节点
 * 2. 检查节点状态是否为"okay"
 * 3. 验证节点兼容性字符串是否为"alientek,key"
 * 4. 获取GPIO引脚配置
 * 5. 获取中断号配置
 *
 * @return 0 - 成功解析所有配置参数
 * @return 负数 - 解析失败，具体错误码如下：
 *         -EINVAL - 节点未找到、状态错误、兼容性错误或中断号获取失败
 *         其他负数 - 设备树属性读取错误码
 */
static int key_parse_dt(void)
{
    int ret;
    const char *str;

    /* 查找设备树中的key节点 */
    key.nd = of_find_node_by_path("/key");
    if (key.nd == NULL)
    {
        printk("key node not found!\r\n");
        return -EINVAL;
    }
    
    /* 读取并验证节点状态属性 */
    ret = of_property_read_string(key.nd, "status", &str);
    if (ret < 0)
    {
        printk("get status property error!\r\n");
        return ret;
    }
    printk("status = %s\r\n", str);
    if (strcmp(str, "okay"))
    {
        printk("key node status error!\r\n");
        return -EINVAL;
    }
    
    /* 读取并验证节点兼容性属性 */
    ret = of_property_read_string(key.nd, "compatible", &str);
    if (ret < 0)
    {
        printk("get compatible property error!\r\n");
        return ret;
    }

    if (strcmp(str, "alientek,key"))
    {
        printk("key node compatible error!\r\n");
        return -EINVAL;
    }

    /* 获取按键GPIO引脚配置 */
    key.key_gpio = of_get_named_gpio(key.nd, "key-gpio", 0);
    if (key.key_gpio < 0)
    {
        printk("get key-gpio error!\r\n");
        return key.key_gpio;
    }
    printk("key_gpio = %d\r\n", key.key_gpio);

    /* 获取按键中断号配置 */
    key.irq_num = irq_of_parse_and_map(key.nd, 0);
    if (!key.irq_num)
    {
        printk("get irq_num error!\r\n");
        return -EINVAL;
    }
    printk("irq_num = %d\r\n", key.irq_num);
    
    return 0;
}

/**
 * @brief 初始化按键GPIO和中断
 * 
 * 该函数完成按键GPIO的申请、配置为输入模式，并注册相应的中断处理函数。
 * 主要包括GPIO申请、设置输入方向、获取中断触发类型、申请中断等操作。
 * 
 * @param 无
 * 
 * @return 成功返回0，失败返回错误码
 *         - 0: 初始化成功
 *         - 负数: GPIO申请失败或中断申请失败的错误码
 */
static int key_gpio_init(void)
{
    int ret;
    unsigned long irq_flags = 0;

    /* 申请按键使用的GPIO */
    ret = gpio_request(key.key_gpio, "key0");
    if (ret)
    {
        printk(KERN_ERR "key: Failed to request key-gpio\n");
        return ret;
    }

    /* 设置GPIO为输入模式 */
    gpio_direction_input(key.key_gpio);

    /* 获取中断触发类型，如果未设置则使用上升沿和下降沿触发 */
    irq_flags = irq_get_trigger_type(key.irq_num);
    if (irq_flags == IRQF_TRIGGER_NONE)
    {
        irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
    }

    /* 申请并注册中断处理函数 */
    ret = request_irq(key.irq_num, key_irq_handler, irq_flags, "key0_irq", NULL);

    if (ret)
    {
        /* 中断申请失败时释放已申请的GPIO */
        gpio_free(key.key_gpio);
        printk(KERN_ERR "key: Failed to request key-irq\n");
        return ret;
    }
    return 0;
}

static void key_timer_function(struct timer_list *t)
{
    static int last_val = 0;
    unsigned long flags;
    int current_val;

    spin_lock_irqsave(&key.lock, flags);

    /* 读取按键值并判断按键当前状态 */
    current_val = gpio_get_value(key.key_gpio);
    if (1 == current_val && !last_val) // 按键按下
    {
        status = KEY_PRESS;
    }
    else if (0 == current_val && last_val) // 按键释放
    {
        status = KEY_RELEASE;
    }
    else // 按键保持
    {
        status = KEY_KEEP;
    }

    last_val = current_val;
    spin_unlock_irqrestore(&key.lock, flags);
}

static int key_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &key;
    return 0;
}

static ssize_t key_read(struct file *filp, char __user *buf, size_t count, loff_t *off)
{
    int ret;
    struct key_dev *dev = filp->private_data;
    unsigned long flags;

    spin_lock_irqsave(&dev->lock, flags);
    ret = copy_to_user(buf, &status, sizeof(status));
    status = KEY_KEEP;
    spin_unlock_irqrestore(&dev->lock, flags);
    return ret;
}

static struct file_operations key_fops =
    {
        .owner = THIS_MODULE,
        .open = key_open,
        .read = key_read,
};

/**
 * @brief 按键驱动初始化函数
 * 
 * 该函数在模块加载时被调用，完成按键设备的完整初始化过程，
 * 包括自旋锁初始化、定时器设置、设备树解析、GPIO初始化、
 * 字符设备注册、设备类和设备节点创建等操作。
 * 
 * @return 成功返回0，失败返回负的错误码
 */
static int __init mykey_init(void)
{
    int ret;
    /* 1、自旋锁初始化 */
    spin_lock_init(&key.lock);

    /* 定时器初始化 */
    timer_setup(&key.timer, key_timer_function, 0);
    
    /* 2、设备树解析 */
    ret = key_parse_dt();
    if (ret < 0)
    {
        return ret;
    }

    /* GPIO初始化 */
    ret = key_gpio_init();
    if (ret < 0)
    {
        return ret;
    }
    
    /* 3、注册字符设备驱动 */
    ret = alloc_chrdev_region(&key.devid, 0, KEY_CNT, KEY_NAME);
    if (ret < 0)
    {
        goto fail_unmap;
    }
    
    /* 字符设备初始化和添加 */
    key.cdev.owner = THIS_MODULE;
    cdev_init(&key.cdev, &key_fops);
    ret = cdev_add(&key.cdev, key.devid, KEY_CNT);
    if (ret < 0)
    {
        goto fail_unregister;
    }
    
    /* 创建设备类 */
    key.class = class_create(THIS_MODULE, KEY_NAME);
    if (IS_ERR(key.class))
    {
        // ret = PTR_ERR(key.class);
        goto fail_unregister;
    }
    
    /* 创建设备节点 */
    key.device = device_create(key.class, NULL, key.devid, NULL, KEY_NAME);
    if (IS_ERR(key.device))
    {
        // ret = PTR_ERR(key.device);
        goto fail_destroy_class;
    }
    return 0;
    
/* 错误处理回退路径 */
fail_destroy_class:
    class_destroy(key.class);
fail_unregister:
    unregister_chrdev_region(key.devid, KEY_CNT);
fail_unmap:
    free_irq(key.irq_num, NULL);
    gpio_free(key.key_gpio);
    return -EIO;
}

static void __exit mykey_exit(void)
{
    cdev_del(&key.cdev);
    unregister_chrdev_region(key.devid, KEY_CNT);
    del_timer_sync(&key.timer);//删除定时器
    device_destroy(key.class, key.devid);
    class_destroy(key.class);
    free_irq(key.irq_num, NULL);
    gpio_free(key.key_gpio);
}

module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JACK");
MODULE_INFO(intree, "Y");